/*
 * @Author: hongbin
 * @Date: 2023-03-15 20:31:29
 * @LastEditors: hongbin
 * @LastEditTime: 2023-03-20 17:29:29
 * @Description: 使用八叉树返回碰撞物体
 */
import Layout from "@/src/components/Three/Layout";
import { ThreeHelper } from "@/src/ThreeHelper";
import { RandomColor } from "@/src/ThreeHelper/utils";
import { FallHelper } from "@/src/ThreeHelper/utils/FallHelper";
import { JumpControls } from "@/src/ThreeHelper/utils/JumpControls";
import { OctreeControls } from "@/src/ThreeHelper/utils/OctreeControls";
import { FC } from "react";
import { Box3, Group, Object3D, Vector2, Vector3 } from "three";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js";

interface IProps {}

const Index: FC<IProps> = () => {
    return (
        <Layout
            title={"八叉树返回碰撞物体"}
            init={init}
            desc="修改八叉树源码让每个三角面绑定所属物体"
        />
    );
};

export default Index;

async function init(helper: ThreeHelper) {
    helper.addAxis();
    helper.addGUI();
    helper.addStats();
    helper.camera.position.set(0, 1, 2);
    helper.frameByFrame();
    helper.initLights();
    const v = new Vector3();
    let prevMeshId = "";

    const jump = new JumpControls();

    const group = new Group();

    const gltf = await helper.loadGltf("/models/collision-world.glb");
    group.add(gltf.scene);

    const { random } = Math;
    for (let i = 0; i < 20; i++) {
        const box = helper.generateRect(
            { width: random(), height: random(), depth: random() * 4 },
            { color: new RandomColor() }
        );
        box.position.x = (0.5 - random()) * 10;
        box.position.z = (0.5 - random()) * 10;
        box.position.y = random();
        box.rotateX(random());
        group.add(box);
        box.name = i + "";
    }

    // const floor = helper.generateRect(
    //     { width: 10, height: 0.1, depth: 10 },
    //     { color: new RandomColor() }
    // );
    // floor.name = "floor";
    // group.add(floor);

    helper.add(group);
    console.log(group);

    const observer = helper.generateRect(
        { width: 0.3, height: 0.3, depth: 0.3 },
        { color: "#f00" }
    );
    helper.add(observer);

    helper.keyBoardListen();
    let w = false;
    helper.listenKey(
        "KeyW",
        () => {
            w = true;
        },
        () => {
            w = false;
        }
    );

    helper.listenKey("Space", () => {
        jump.jump();
    });

    jump.onUpdate((increment) => {
        v.y += increment;
    });

    let s = false;
    helper.listenKey(
        "KeyS",
        () => {
            s = true;
        },
        () => {
            s = false;
        }
    );
    let a = false;
    helper.listenKey(
        "KeyA",
        () => {
            a = true;
        },
        () => {
            a = false;
        }
    );
    let d = false;
    helper.listenKey(
        "KeyD",
        () => {
            d = true;
        },
        () => {
            d = false;
        }
    );

    const octree = new OctreeControls();
    const size = new Box3().setFromObject(observer).getSize(new Vector3());
    observer.userData._size = size;
    octree.player(observer);
    observer.position.y += octree.playerCollider.radius;
    octree.fromGraphNode(group);
    octree.console(helper.gui);
    octree.collide((res) => {
        if (res) {
            if (res.normal.y == -1) {
                jump.bumpHead();
            }

            const v = res.normal.multiplyScalar(res.depth);
            octree.translatePlayerCollider(v);
            // observer.position.add(v);
            if (res.meshName === "floor") return;
            if (res.meshuuid && prevMeshId != res.meshuuid) {
                const mesh = helper.scene.getObjectByProperty(
                    "uuid",
                    res.meshuuid + ""
                ) as Mesh;
                if (mesh) {
                    //@ts-ignore
                    mesh.material.color = new RandomColor();
                    prevMeshId = res.meshuuid;
                    outlinePass.selectedObjects = [mesh];
                }
            }
        }
    });

    const composer = new EffectComposer(helper.renderer);
    const renderPass = new RenderPass(helper.scene, helper.camera);
    composer.addPass(renderPass);

    const outlinePass = new OutlinePass(
        new Vector2(
            helper.renderer.domElement.offsetWidth,
            helper.renderer.domElement.offsetHeight
        ),
        helper.scene,
        helper.camera
    );
    composer.addPass(outlinePass);

    //抗锯齿shader
    const effectFXAA = new ShaderPass(FXAAShader);
    effectFXAA.uniforms["resolution"].value.set(
        1 / helper.renderer.domElement.offsetWidth,
        1 / helper.renderer.domElement.offsetHeight
    );
    composer.addPass(effectFXAA);

    const diff = new Vector3();

    const fall = new FallHelper();

    helper.render = () => {
        composer.render();
    };

    helper.animation(() => {
        const delta = helper.clock.getDelta();
        diff.copy(octree.playerCollider.start);
        const angle = helper.controls.getAzimuthalAngle();

        console.log(octree.playerOnFloor);
        if (
            !octree.playerOnFloor &&
            observer.position.y > -20 &&
            !jump.jumping
        ) {
            // const height = fall.computeDistance(delta) / 2;
            const height = 0.1;
            v.y -= height;
        } else {
            fall.resetTime();
        }

        jump.update();
        if (w) {
            v.z -= 0.04;
        }
        if (a) {
            v.x -= 0.04;
        }
        if (s) {
            v.z += 0.04;
        }
        if (d) {
            v.x += 0.04;
        }

        v.applyAxisAngle(Object3D.DefaultUp, angle);
        octree.translatePlayerCollider(v);

        octree.playerCollisions();
        diff.sub(octree.playerCollider.start);
        observer.position.sub(diff);
        helper.camera.position.sub(diff);
        helper.controls.target.sub(diff);
        v.set(0, 0, 0);

        if (observer.position.y < -20) {
            observer.position.y = 10;
        }
    });
}
